package com.xiaoyu.lib_av.manager

import `in`.srain.cube.cache.DiskFileUtils
import `in`.srain.cube.concurrent.SimpleTask
import `in`.srain.cube.util.CLog
import com.xiaoyu.base.app.AppContext

import com.xiaoyu.lib_av.datamodel.AVTokenInfo
import com.xiaoyu.lib_av.datamodel.CallVendorType
import com.xiaoyu.lib_av.proxy.AgoraAbstractProxy
import com.xiaoyu.base.AppConfig
import com.xiaoyu.base.data.UserData
import io.agora.rtc.Constants.LOG_FILTER_INFO
import io.agora.rtc.IRtcEngineEventHandler
import io.agora.rtc.RtcEngine
import io.agora.rtm.*
import java.io.File

class AgoraManager private constructor() {

    val rtmClient: RtmClient? by lazy {
        var rtmClient: RtmClient? = null
        try {
            rtmClient = RtmClient.createInstance(AppContext.getContext(), AppConfig.agoraAppId(), rtmClientListener)
            rtmClient.setLogFilter(LOG_FILTER_INFO)
            rtmClient.setLogFile(AGORA_RTM_LOG_PATH)
        } catch (t: Throwable) {
            t.printStackTrace()
        }
        rtmClient
    }

    var mRtcEngine: RtcEngine? = null

    val callingIdSet = mutableSetOf<String>()

    private var mAgoraProxy: AgoraAbstractProxy? = null

    private var mLoginRunnable: Runnable? = null

    val vendorType: String
        @CallVendorType.CallVendorTypeDef
        get() = VENDOR_TYPE

    val logDir: File
        get() = LOG_CACHE_DIR.path

    val lastRecordFileName: String
        get() = AGORA_LAST_CALL_RECORD_WAV

    val isCalling: Boolean
        get() = callingIdSet.isNotEmpty()

    fun isCalling(id: String?): Boolean {
        return callingIdSet.contains(id)
    }

    private val rtmClientListener = object : RtmClientListener {
        override fun onPeersOnlineStatusChanged(p0: MutableMap<String, Int>?) {
        }

        override fun onMediaUploadingProgress(p0: RtmMediaOperationProgress?, p1: Long) {
        }

        override fun onImageMessageReceivedFromPeer(p0: RtmImageMessage?, p1: String?) {
        }

        override fun onConnectionStateChanged(state: Int, reason: Int) {
            mAgoraProxy?.rtmClientListener?.onConnectionStateChanged(state, reason)
        }

        override fun onMessageReceived(rtmMessage: RtmMessage, s: String) {
            mAgoraProxy?.rtmClientListener?.onMessageReceived(rtmMessage, s)
        }

        override fun onFileMessageReceivedFromPeer(p0: RtmFileMessage?, p1: String?) {
        }

        override fun onMediaDownloadingProgress(p0: RtmMediaOperationProgress?, p1: Long) {
        }

        override fun onTokenExpired() {
            mAgoraProxy?.rtmClientListener?.onTokenExpired()
        }
    }

    private val rtcEngineEventHandler = object : IRtcEngineEventHandler() {
        override fun onWarning(warn: Int) {
            mAgoraProxy?.rtcEngineEventHandler?.onWarning(warn)
        }

        override fun onConnectionStateChanged(state: Int, reason: Int) {
            mAgoraProxy?.rtcEngineEventHandler?.onConnectionStateChanged(state, reason)
        }

        override fun onError(err: Int) {
            mAgoraProxy?.rtcEngineEventHandler?.onError(err)
        }

        override fun onJoinChannelSuccess(channel: String?, uid: Int, elapsed: Int) {
            mAgoraProxy?.rtcEngineEventHandler?.onJoinChannelSuccess(channel, uid, elapsed)
        }

        override fun onRejoinChannelSuccess(channel: String?, uid: Int, elapsed: Int) {
            mAgoraProxy?.rtcEngineEventHandler?.onRejoinChannelSuccess(channel, uid, elapsed)
        }

        override fun onLeaveChannel(stats: RtcStats?) {
            mAgoraProxy?.rtcEngineEventHandler?.onLeaveChannel(stats)
        }

        override fun onUserJoined(uid: Int, elapsed: Int) {
            mAgoraProxy?.rtcEngineEventHandler?.onUserJoined(uid, elapsed)
        }

        override fun onUserOffline(uid: Int, reason: Int) {
            mAgoraProxy?.rtcEngineEventHandler?.onUserOffline(uid, reason)
        }

        override fun onNetworkQuality(uid: Int, txQuality: Int, rxQuality: Int) {
            mAgoraProxy?.rtcEngineEventHandler?.onNetworkQuality(uid, txQuality, rxQuality)
        }

        override fun onAudioRouteChanged(routing: Int) {
            mAgoraProxy?.rtcEngineEventHandler?.onAudioRouteChanged(routing)
        }

        override fun onClientRoleChanged(oldRole: Int, newRole: Int) {
            mAgoraProxy?.rtcEngineEventHandler?.onClientRoleChanged(oldRole, newRole)
        }

        override fun onRtcStats(stats: RtcStats?) {
            mAgoraProxy?.rtcEngineEventHandler?.onRtcStats(stats)
        }

        override fun onAudioVolumeIndication(speakers: Array<out AudioVolumeInfo>?, totalVolume: Int) {
            mAgoraProxy?.rtcEngineEventHandler?.onAudioVolumeIndication(speakers, totalVolume)
        }

        override fun onLocalAudioStateChanged(p0: Int, p1: Int) {
            mAgoraProxy?.rtcEngineEventHandler?.onLocalAudioStateChanged(p0, p1)
        }

        override fun onRemoteAudioStateChanged(p0: Int, p1: Int, p2: Int, p3: Int) {
            mAgoraProxy?.rtcEngineEventHandler?.onRemoteAudioStateChanged(p0, p1, p2, p3)
        }

        override fun onLocalVideoStateChanged(p0: Int, p1: Int) {
            mAgoraProxy?.rtcEngineEventHandler?.onLocalVideoStateChanged(p0, p1)
        }

        override fun onRemoteVideoStateChanged(p0: Int, p1: Int, p2: Int, p3: Int) {
            mAgoraProxy?.rtcEngineEventHandler?.onRemoteVideoStateChanged(p0, p1, p2, p3)
        }
    }

    fun tryCreateRtcEngine(channelProfile: Int): RtcEngine? {
        try {
            mRtcEngine = RtcEngine.create(AppContext.getContext(), AppConfig.agoraAppId(), rtcEngineEventHandler)
            mRtcEngine?.setLogFilter(LOG_FILTER_INFO)
            mRtcEngine?.setLogFile(AGORA_RTC_LOG_PATH)
            mRtcEngine?.setChannelProfile(channelProfile)
        } catch (t: Throwable) {
            t.printStackTrace()
        } finally {
            return mRtcEngine
        }
    }

    fun setAgoraProxy(agoraProxy: AgoraAbstractProxy?) {
        mAgoraProxy = agoraProxy
        if (mAgoraProxy == null) {
            callingIdSet.clear()
        }
    }

    fun login(loginRunnable: Runnable) {
        mLoginRunnable = loginRunnable
        mLoginRunnable?.run()
    }

    fun logout() {
        rtmClient?.logout(null)
    }

    fun login(tokenInfo: AVTokenInfo) {
        rtmClient?.login(tokenInfo.token, UserData.getInstance().uid, object : ResultCallback<Void?> {
            override fun onSuccess(aVoid: Void?) {
                CLog.i(TAG, "login success, token=${tokenInfo.token}, uid=${UserData.getInstance().uid}")
            }

            override fun onFailure(errorInfo: ErrorInfo) {
                CLog.e(TAG, "login failure, code: %s, reason: %s", errorInfo.errorCode, errorInfo.errorDescription)
                if (errorInfo.errorCode != RtmStatusCode.LoginError.LOGIN_ERR_ALREADY_LOGIN) {
                    getTokenFromServerDelay()
                }
            }
        })
    }

    fun getTokenFromServerDelay() {
        mLoginRunnable?.let {
            SimpleTask.postDelay({ (it.run()) }, RETRY_REQUEST_TOKEN_DELAY_MS.toLong())
        }
    }

    companion object {
        const val TAG = "lib-av"
        private const val VENDOR_TYPE = CallVendorType.AGORA
        private const val LOG_DIR_NAME = VENDOR_TYPE + "_log"
        private const val LOG_SIZE_IN_KB: Long = 1024
        private const val RETRY_REQUEST_TOKEN_DELAY_MS = 10 * 1000
        private const val AGORA_LAST_CALL_RECORD_WAV = "agora-last-call-record.aac"

        private val LOG_CACHE_DIR = DiskFileUtils.getDiskCacheDir(AppContext.getContext(), LOG_DIR_NAME, LOG_SIZE_IN_KB)
        private val AGORA_RTM_LOG_PATH = LOG_CACHE_DIR.path.toString() + File.separator + "agora-sdk-rtm.log"
        private val AGORA_RTC_LOG_PATH = LOG_CACHE_DIR.path.toString() + File.separator + "agora-sdk-rtc.log"

        private object SingletonHolder {
            val INSTANCE = AgoraManager()
        }

        fun getInstance(): AgoraManager {
            return SingletonHolder.INSTANCE
        }
    }
}
